/* Copyright 2023 Man Group Operations Limited
 *
 * Use of this software is governed by the Business Source License 1.1 included in the file licenses/BSL.txt.
 *
 * As of the Change Date specified in that file, in accordance with the Business Source License, use of this software will be governed by the Apache License, version 2.0.
 */

#pragma once

#include <sstream>
#include <iomanip>

// based on this: https://codereview.stackexchange.com/questions/165120/printing-hex-dumps-for-diagnostics
namespace arcticdb {
inline std::ostream &hex_dump(std::ostream &os, const void *buffer,
                       std::size_t buf_size, bool show_printable_chars = true) {
    if (buffer == nullptr) {
        return os;
    }
    auto old_format = os.flags();
    auto old_fill_char = os.fill();
    constexpr std::size_t max_line{8};
    // create a place to store text version of string
    char render_string[max_line + 1];
    char *rsptr{render_string};
    // convenience cast
    const unsigned char *buf{reinterpret_cast<const unsigned char *>(buffer)};

    for (std::size_t line_count = max_line; buf_size; --buf_size, ++buf) {
        os << std::setw(2) << std::setfill('0') << std::hex
           << static_cast<unsigned>(*buf) << ' ';
        *rsptr++ = std::isprint(*buf) ? *buf : '.';
        if (--line_count == 0) {
            *rsptr++ = '\0';  // terminate string
            if (show_printable_chars) {
                os << " | " << render_string;
            }
            os << '\n';
            rsptr = render_string;
            line_count = std::min(max_line, buf_size);
        }
    }
    // emit newline if we haven't already
    if (rsptr != render_string) {
        if (show_printable_chars) {
            for (*rsptr++ = '\0'; rsptr != &render_string[max_line + 1]; ++rsptr) {
                os << "   ";
            }
            os << " | " << render_string;
        }
        os << '\n';
    }

    os.fill(old_fill_char);
    os.flags(old_format);
    return os;
}

inline std::string dump_bytes(const void* data, size_t size, size_t max_size = 20u) {
    const auto sz = std::min(max_size, size);
    std::ostringstream strm;
    hex_dump(strm, data, sz);
    return strm.str();
}

} //namespace arcticdb